import React, { useEffect, useState } from 'react'
import { Upload as AntUpload, Button, message, Modal } from 'antd'
import { PlusOutlined, InboxOutlined } from '@ant-design/icons'
import type { UploadProps, RcFile, UploadChangeParam } from 'antd/lib/upload'

import { checkImg, ImgDimension, ImgSize, getBase64 } from './image'

import { createUid } from './helper'

import {
  isFunction,
  isEmpty,
  isString,
  isObject,
  isBoolean,
  get,
  cloneDeep
} from 'lodash'

const errMsg = {
  dimension: '尺寸不符合标准',
  size: '大小不符合标准'
}

export const uploadConfig = {
  url: '//dripworks-api.jd.com/api/uploadImage',
}

type UploadFile = UploadChangeParam['fileList'][0] & { [key: string]: any }

type RealUpload = {
  action?: string
  respFieldKey: string
  data?: Record<string, unknown>
  headers?: Record<string, string>
  method?: UploadProps['method']
  name?: string
  withCredentials?: boolean
}
type CustomBeforeUpload = (
  file: RcFile,
  fileList: RcFile[],
  callback: UploadProps['beforeUpload'],
  LIST_IGNORE: typeof AntUpload.LIST_IGNORE
) => Promise<boolean | typeof AntUpload.LIST_IGNORE>

type CustomAfterUpload = (
  file: UploadFile,
  fileList: UploadFile[]
) => UploadFile | false

type PreviewModal = {
  width?: number
  height?: number
  title?: string
}

interface Props extends Omit<UploadProps, 'beforeUpload' | 'onChange'> {
  uploadType?: 'upload' | 'image'
  value?: UploadProps['fileList'] | Array<UploadFile> | string[] | string
  onChange?: (e: Props['value']) => void;
  changeString?: boolean
  useTaskUpload?: boolean
  imgDimension?: Partial<ImgDimension>
  imgSize?: Partial<ImgSize>
  previewModal?: Partial<PreviewModal>
  isDragMode?: boolean
  buttonText?: string
  realUpload?: RealUpload // 实时上传
  beforeUpload?: CustomBeforeUpload
  afterUpload?: CustomAfterUpload
}

const Upload: React.FC<Props> = ((props) => {
  const {
    uploadType = 'image',
    realUpload,
    buttonText,
    isDragMode,
    imgDimension,
    imgSize,
    previewModal,
    beforeUpload,
    afterUpload,
    value,
    onChange,
    changeString,
    disabled,
    ...otherProps
  } = props

  // 进入时或者变换时转换
  const transferTo = (data: Props['value']): UploadFile[] => {
    const uploadData: any =
      typeof data === 'string' && data ? data.split(',') : data
    return (uploadData || [])?.map(
      (e: UploadFile | string | Record<string, unknown>) => {
        const duid = createUid(16)
        if (isString(e)) {
          // 返回的字符串链接对象
          const resp = {
            name: e,
            uid: e, // 生成uid
            url: e as string
          }
          resp[realUpload?.respFieldKey || ''] = e
          return resp
        } else if (isObject(e)) {
          // 受控模式下的文件对象
          if (e instanceof File) {
            return {
              name: e.name,
              size: e.size,
              type: e.type,
              uid: (e.uid as string) || duid,
              percent: e.percent as number,
              status: e.status as
                | 'error'
                | 'success'
                | 'done'
                | 'uploading'
                | 'removed',
              originFileObj: e as RcFile
            }
            // 受控模式下的返回值
          } else {
            const newData = cloneDeep(e as UploadFile)
            if (!e.name) newData.name = duid
            if (!e?.uid) newData.uid = duid
            return newData
          }
        }
        return {
          name: duid,
          uid: duid, // 生成uid
          url: e as string
        }
      }
    )
  }

  const [fileList, setFileList] = useState<UploadFile[]>(transferTo(value || []))

  const [previewOption, setPreviewOption] = useState({
    open: false,
    src: '',
    title: previewModal?.title || ''
  })

  useEffect(() => {
    setFileList(transferTo(value || []))
  }, [value])

  // 上传前diemension、size校验以及自定义方法，非图片不会有这两个参数
  const _beforeUpload: UploadProps['beforeUpload'] = async (file) => {
    const defaultFun = async (file: RcFile): Promise<string | boolean> => {
      if (!imgDimension && !imgSize) return true

      const { isOk, errors } = await checkImg({
        file,
        dimension: imgDimension,
        size: imgSize,
        errMsg
      })
      if (isOk) {
        if (realUpload?.action) return true
        else return false
      } else {
        message.error(errors[0] || '上传失败，有限制')
        return AntUpload.LIST_IGNORE
      }
    }
    try {
      const res = await defaultFun(file)
      if (isBoolean(res)) {
        if (realUpload?.action) return true
        else return false
      }
      return res
    } catch (error) {
      console.error('error')
      message.error('上传错误')
      return AntUpload.LIST_IGNORE
    }
  }

  const _onChange: UploadProps['onChange'] = ({
    file,
    fileList: list
  }: UploadChangeParam) => {
    // 开启实时上传后，需要对返回值赋值]
    if (realUpload?.action) {
      if (file.status === 'done') {
        if (!isEmpty(file.response)) {
          const origin = file.originFileObj!

          let fileData: UploadFile = {
            name: origin.name,
            percent: 100,
            status: 'done',
            uid: origin.uid
          }
          
          fileData = {
            ...fileData,
            ...file.response
          }

          // 不能直接对fileList进行赋值操作，因为内部状态是由immer进行维护的
          const index = list.findIndex((e) => e.uid === fileData.uid)
          if (index >= 0) list[index] = fileData
        }
      }
    }
    // 当文件变化时
    for (const i in list) {
      // 找到当前修改的file，将fileList中的这一项进行替换
      if (!isEmpty(list[i].originFileObj)) {
        list[i] = list[i].originFileObj!
      }
    }
    isFunction(onChange) &&
      onChange(
          list.map((item) => {
            const upLoadResp = get(item, realUpload?.respFieldKey || '')
            if (
              realUpload?.respFieldKey &&
              upLoadResp
            ) {
              return upLoadResp
            }
            if (file.status !== 'done') {
              if (item.uid === file.uid) {
                item.status = file.status
                item.percent = file.percent
              }
              return item
            }
            return item
          })
        )
    }
  // 暂时先改成一直显示
  const isShowBtn = true

  // 图片如果无值则取base64预览
  const handlePreview: UploadProps['onPreview'] = async (f: UploadFile) => {
    const defaultPreview = async (file: UploadFile) => {
      if (!file.url && !file.preview) {
        file.preview = await getBase64(file.originFileObj as RcFile)
      }
      setPreviewOption({
        open: true,
        src: file.url || (file.preview as string),
        title: previewModal?.title || file.name || '图片预览'
      })
    }

    if (uploadType === 'image') {
      await defaultPreview(f)
    }
  }
  
  return (
    <>
      {isDragMode ? (
        <AntUpload.Dragger
          fileList={fileList}
          {...otherProps}
          beforeUpload={_beforeUpload}
          onChange={_onChange}
          onPreview={handlePreview}
          {...realUpload}
          name={realUpload?.name || ''}>
          {isShowBtn ? (
            <>
              <p>
                <InboxOutlined />
              </p>
              <p>{buttonText || '点击或拖拽至该区域'}</p>
            </>
          ) : null}
        </AntUpload.Dragger>
      ) : (
        <AntUpload
          fileList={fileList}
          {...otherProps}
          beforeUpload={_beforeUpload}
          onChange={_onChange}
          onPreview={handlePreview}
          {...realUpload}>
          {otherProps.listType === 'picture-card' ? (
            isShowBtn ? (
              <div>
                <PlusOutlined />
                <div>{buttonText || '点击上传图片'}</div>
              </div>
            ) : null
          ) : isShowBtn ? (
            <Button disabled={disabled}>{buttonText || '点击上传'}</Button>
          ) : null}
        </AntUpload>
      )}
      <Modal
        open={previewOption.open}
        title={'预览图片'}
        footer={null}
        onCancel={() => setPreviewOption({ open: false, src: '', title: '' })}>
        <img alt={previewOption.src} style={{ width: '100%' }} src={previewOption.src} />
      </Modal>
    </>
  )
})

Upload.displayName = 'Upload'

export default Upload
